/**
 * Copyright (C) 2013-2014 the original author or authors.
 */
package com.foreveross.bsl.push.application.impl.channel.apns;

import java.util.Date;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.foreveross.bsl.push.application.CheckinMgmtService;
import com.foreveross.bsl.push.application.impl.DsRouterHelper;
import com.foreveross.bsl.push.application.impl.DsRouterPushId;
import com.foreveross.bsl.push.application.impl.channel.SendFeedbackable;
import com.foreveross.bsl.push.application.impl.channel.PushBinding;
import com.notnoop.apns.ApnsDelegate;
import com.notnoop.apns.ApnsNotification;
import com.notnoop.apns.DeliveryError;
import com.notnoop.apns.internal.Utilities;
import com.notnoop.exceptions.ApnsDeliveryErrorException;
import com.notnoop.exceptions.NetworkIOException;
import com.notnoop.exceptions.RuntimeIOException;

/**
 * apns消息回调策略：失败重发、消息回执机制、消息状态等控制
 *
 * @author Wangyi
 * @version v1.0
 *
 * @date 2013-8-24
 *
 */
@Component
public class ApnsFeedbackDelegate implements ApnsDelegate {
	
	private final static Logger log = LoggerFactory.getLogger(ApnsFeedbackDelegate.class);
	
	@Inject
	private SendFeedbackable feedbacker;
	
	@Inject
	private CheckinMgmtService checkinMgmtService;
	
	private final PushBinding pushBinding = new PushBinding();
	
	/**
	 * @return the tokenPushLinker
	 */
	public PushBinding getPushBinding() {
		return pushBinding;
	}

	/* (non-Javadoc)
	 * @see com.notnoop.apns.ApnsDelegate#messageSent(com.notnoop.apns.ApnsNotification, boolean)
	 */
	@Override
	public void messageSent(ApnsNotification message, boolean resent) {
		DsRouterPushId pushId = this.pushBinding.pop(message.getIdentifier() + "");
		log.debug("获取PushId:{}, 通过Apns消息Id:{}", pushId, message.getIdentifier());
		feedbacker.sentSuccessfully(new Date(), pushId);
	}

	/* (non-Javadoc)
	 * @see com.notnoop.apns.ApnsDelegate#messageSendFailed(com.notnoop.apns.ApnsNotification, java.lang.Throwable)
	 */
	@Override
	public void messageSendFailed(ApnsNotification message, Throwable e) {
		log.error("messageSendFailed() msg: " + message, e);
		if(message == null){
			return;
		}
		String token = Utilities.encodeHex(message.getDeviceToken());
		DsRouterPushId pushId = this.pushBinding.pop(message.getIdentifier() + "");
		if(e instanceof ApnsDeliveryErrorException){
			ApnsDeliveryErrorException deliveryException=(ApnsDeliveryErrorException)e;
			DeliveryError deliveryError=deliveryException.getDeliveryError();
			if(deliveryError.equals(DeliveryError.NO_ERROR) || deliveryError.equals(DeliveryError.PROCESSING_ERROR) ||
					deliveryError.equals(DeliveryError.NONE)) {
				//TODO 不确定这些错误的具体含义
				feedbacker.communicationFailure(deliveryError.name(), deliveryException, pushId);
			} else{
				feedbacker.unrecoverableFailure(deliveryError.name(), deliveryException, pushId);
				if(deliveryError.equals(DeliveryError.INVALID_TOKEN) || deliveryError.equals(DeliveryError.INVALID_TOKEN_SIZE)){
					DsRouterHelper.setCurrentDsRouter(pushId.getDsRouter());
					int n=this.checkinMgmtService.clearToken(token);
					log.info("清除{}条设备签到的无效token:{}", n, token);
				}
			}
		} else if(e instanceof RuntimeIOException){
			feedbacker.couldRetryFailure(e.getMessage(), e, pushId);
		} else if(e instanceof NetworkIOException){
			feedbacker.communicationFailure(e.getMessage(), e, pushId);
		} else{
			feedbacker.unrecoverableFailure(e.getMessage(), e, pushId);
		}
	}

	/* (non-Javadoc)
	 * @see com.notnoop.apns.ApnsDelegate#connectionClosed(com.notnoop.apns.DeliveryError, int)
	 */
	@Override
	public void connectionClosed(DeliveryError e, int messageIdentifier) {
		log.error("connectionClosed() e:{}, msgId:{}", e, messageIdentifier);
		if(messageIdentifier!=-1){
			// resend
			DsRouterPushId pushId = this.pushBinding.pop(messageIdentifier + "");
			feedbacker.couldRetryFailure("connectionClosed()", new ApnsDeliveryErrorException(e), pushId);
		}
	}

	/* (non-Javadoc)
	 * @see com.notnoop.apns.ApnsDelegate#cacheLengthExceeded(int)
	 */
	@Override
	public void cacheLengthExceeded(int newCacheLength) {
		log.debug("cacheLengthExceeded() new cache length:{}", newCacheLength);
	}

	/* (non-Javadoc)
	 * @see com.notnoop.apns.ApnsDelegate#notificationsResent(int)
	 */
	@Override
	public void notificationsResent(int resendCount) {
		log.debug("notificationsResent() resendCount:{}", resendCount);		
	}
}
